using System;
using System.Collections.Generic;
using System.Linq;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Audio;
using Microsoft.Xna.Framework.Content;
using Microsoft.Xna.Framework.GamerServices;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;
using Microsoft.Xna.Framework.Media;

namespace Terrain
{
    public class Terrain : GameModels.ModelBase
    {
        public Terrain(Game game, Texture2D mapHeight,Texture2D ground,Texture2D grass,Texture2D snow, float mCellSize, int iMaxHeight, Effect terrainEffect)
            : base(game)
        {
            map = mapHeight;
            width = map.Width;
            length = map.Height;
            maxHeight = iMaxHeight;
            cellSize = mCellSize;
            effect = terrainEffect;
            device = game.GraphicsDevice;
            groundTexture = ground;
            grassTexture = grass;
            snowTexture = snow;
            GenerateTerrain();
        }
        private GraphicsDevice device;
        private Texture2D groundTexture;
        private Texture2D snowTexture;
        private Texture2D grassTexture;
        private VertexPositionNormalTexture[] vertices;
        private short[] indices;
        private VertexBuffer vBuffer;
        private IndexBuffer idxBuffer;
        private int width, length;
        private int nVertices, nIndices;
        private float cellSize;
        private float maxHeight;
        private float[,] heights;
        private Texture2D map;
        private Effect effect;
        Color[] heightMapData;
        private void GenerateTerrain()
        {
            InitializeVariable();
            GenerateHeights();
            GenerateVertices();
            GenerateIndices();
            GenerateNormals();
        }
        private void InitializeVariable()
        {
            nVertices = length * width;
            nIndices = (length - 1) * (width - 1) * 6;
            vBuffer = new VertexBuffer(device, typeof(VertexPositionNormalTexture), nVertices, BufferUsage.WriteOnly);
            idxBuffer = new IndexBuffer(device, IndexElementSize.SixteenBits, nIndices, BufferUsage.WriteOnly);
        }
        private void GenerateHeights()
        {
            heightMapData = new Color[nVertices];
            heights = new float[width, length];
            map.GetData<Color>(heightMapData);
            for (int i = 0; i < width; i++)
            {
                for (int j = 0; j < length; j++)
                {
                    heights[i, j] = (heightMapData[i + j * width].R - 50) * maxHeight / 205;
                }
            }
        }
        private void GenerateVertices()
        {
            Vector3 centerTransform = -new Vector3(width / 2 * cellSize, 0, length / 2 * cellSize);
            vertices = new VertexPositionNormalTexture[nVertices];
            for (int i = 0; i < width; i++)
            {
                for (int j = 0; j < length; j++)
                {
                    Vector3 postion = new Vector3(i * cellSize, heights[i, j], j * cellSize) + centerTransform;
                    Vector2 uv = new Vector2((float)i / width, (float)j / length);
                    vertices[i + j * width] = new VertexPositionNormalTexture(postion, Vector3.Zero, uv);
                }
            }
        }
        private void GenerateIndices()
        {
            indices = new short[nIndices];
            int index = 0;
            for (int i = 0; i < width - 1; i++)
            {
                for (int j = 0; j < length - 1; j++)
                {
                    short vertexIndex = (short)(i + j * width);
                    indices[index++] = vertexIndex;
                    indices[index++] = (short)(vertexIndex + 1);
                    indices[index++] = (short)(vertexIndex + 1 + width);

                    indices[index++] = (short)(vertexIndex + 1 + width);
                    indices[index++] = (short)(vertexIndex + width);
                    indices[index++] = vertexIndex;
                }
            }
        }
        private void GenerateNormals()
        {
            for (int i = 0; i < nIndices - 3; i += 3)
            {
                Vector3 v1 = vertices[indices[i]].Position;
                Vector3 v2 = vertices[indices[i + 1]].Position;
                Vector3 v3 = vertices[indices[i + 2]].Position;

                Vector3 normal = Vector3.Cross(v1 - v2, v1 - v3);
                normal.Normalize();

                vertices[indices[i]].Normal = normal;
                vertices[indices[i + 1]].Normal = normal;
                vertices[indices[i + 2]].Normal = normal;
            }
        }
        public override void Draw(GameTime gameTime)
        {
            //effect.Parameters["World"].SetValue(Matrix.CreateTranslation(Position));
            effect.Parameters["View"].SetValue(View);
            effect.Parameters["Projection"].SetValue(Projection);
            effect.Parameters["GroundTexture"].SetValue(groundTexture);
            effect.Parameters["GrassTexture"].SetValue(grassTexture);
            effect.Parameters["SnowTexture"].SetValue(snowTexture);
            foreach (EffectPass pass in effect.CurrentTechnique.Passes)
            {
                pass.Apply(); 
                device.DrawUserIndexedPrimitives<VertexPositionNormalTexture>(PrimitiveType.TriangleList, vertices, 0, nVertices, indices, 0, nIndices / 3);           
            }
            base.Draw(gameTime);
        }
    }
}
